

#include "game_replay.h"

using namespace WtfEngine;

void Replay::StartPlayback()
{
	Kernel::GetInstance().BindEvent( new FrameStartEvent(),
		new ObjFunctor<Replay, void>( this, &Replay::PlayFrame ) );

	// Save original game state for restoration when playback is stopped
	mrOriginalState = GameManager::GetInstance().SerializeGame();
};

void Replay::StopPlayback()
{
	Kernel::GetInstance().RemoveHandler(
		new ObjFunctor<Replay, void>( this, &Replay::PlayFrame ) );
	GameManager::GetInstance().DeserializeGame( mrOriginalState, GameManager::GAME_REPLACE );
	Kernel::GetInstance().ProcessEvent( new ReplayStopEvent(this) );
};

void Replay::StartRecording( unsigned maxFrames )
{
	mMaxFrames = maxFrames;
	Kernel::GetInstance().BindEvent( new FrameEndEvent(),
		new ObjFunctor<Replay, void>( this, &Replay::SaveFrame ) );
};

void Replay::StopRecording()
{
	Kernel::GetInstance().RemoveHandler(
		new ObjFunctor<Replay, void>( this, &Replay::SaveFrame ) );
};


/**
 * Plays a single frame at the current position. Called upon frame
 * start by the game state when playback is enabled.
 **/
void Replay::PlayFrame()
{
	if( mPositionIndex >= mFrames.size() ) {
		// End of playback
		StopPlayback();
	} else if( mFrames[mPositionIndex].first <= mPosition ) {
		// Advance one frame
		GameManager::GetInstance().DeserializeGame( mFrames[mPositionIndex].second, GameManager::GAME_REPLACE );
		mPositionIndex++;
	}
	mPosition += Kernel::GetInstance().GetGameState()->GetDelta();
};

void Replay::SaveFrame()
{
	if( mFrames.empty() ) {
		mPosition = 0.0;
	} else {
		mPosition += Kernel::GetInstance().GetGameState()->GetDelta();
	}

	mFrames.push_back(tFrame( mPosition, GameManager::GetInstance().SerializeGame() ));

	// Drop frames if too large
	if(mFrames.size() > mMaxFrames) {
		unsigned c = (mMaxFrames / 4) < 64 ? (mMaxFrames / 4) : 64;
		for(int i = 0; i < c; i++) {
			mFrames.erase(mFrames.begin());
		};

		tGameTime dt = mFrames.front().first;
		for(tFrameSet::iterator i = mFrames.begin(); i != mFrames.end(); i++) {
			i->first -= dt;
		};
		mPosition -= dt;
	};
};

void Replay::Reset()
{
	mPosition = 0.0;
	mPositionIndex = 0;
};

void Replay::Clear()
{
	mFrames.clear();
	Reset();
};

/**
 * Sets playback position to the specified time (relative to replay start as zero)
 **/
void Replay::Seek(tGameTime t)
{
	unsigned a = 0, b = mFrames.size();
	while( a != b ) {
		unsigned m = (a + b) / 2;
		if( t < mFrames[m].first ) {
			b = m;
		} else if( t > mFrames[m].first ) {
			a = m + 1;
		} else {
			break;
		};
	};
	mPositionIndex = a;
	mPosition = t;
};

void Replay::SaveToFile( const String& sFile )
{
	// TODO
};

WtfEngine::IDataObject * Replay::Load(const StringPool::Entry& sFilePath)
{
	// TODO
	return new Replay(sFilePath);
};


